package com.easypan.aspect;

import com.easypan.annotation.GlobalInterceptor;
import com.easypan.annotation.VerifyParam;
import com.easypan.entity.constants.Constants;
import com.easypan.entity.dto.SessionWebUserDto;
import com.easypan.entity.enums.ResponseCodeEnum;
import com.easypan.exception.BusinessException;
import com.easypan.utils.StringTools;
import com.easypan.utils.VerifyUtils;
import org.apache.catalina.connector.Request;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@Aspect
@Component
public class GlobalOperationAspect {

    private static final Logger logger = LoggerFactory.getLogger(GlobalOperationAspect.class);
    private static final String[] TYPE_BASE = {"java.lang.String", "java.lang.Integer", "java.lang.Long"};

    // 切点：加入@GlobalInterceptor注解的就拦截
    @Pointcut("@annotation(com.easypan.annotation.GlobalInterceptor)")
    private void requestInterceptor(){
    }

    @Before("requestInterceptor()")
    public void interceptorDo(JoinPoint point) throws BusinessException {
        try {
            String methodName = point.getSignature().getName();
            Object target = point.getTarget();
            Object[] arguments = point.getArgs();
            Class<?>[] parameterTypes = ((MethodSignature)point.getSignature()).getMethod().getParameterTypes();
            Method method = target.getClass().getMethod(methodName, parameterTypes);
            GlobalInterceptor interceptor = method.getAnnotation(GlobalInterceptor.class);

            if (null == interceptor) {
                return;
            }
            // 校验登录
            if (interceptor.checkLogin() || interceptor.checkAdmin()) {
                if (interceptor.checkLogin() || interceptor.checkAdmin()) {
                    checkLogin(interceptor.checkAdmin());
                }
            }
            // 校验参数
            if (interceptor.checkParams()) {
                validateParams(method, arguments);
            }
//            // 拦截器校验完，执行方法
//            Object pointResult = point.proceed();
//            return pointResult;
        } catch (BusinessException e) {
            logger.error("全局拦截器异常", e);
            throw e;
        } catch (Exception e) {
            logger.error("全局拦截器异常", e);
            throw new BusinessException(ResponseCodeEnum.CODE_500);
        } catch (Throwable e) {
            logger.error("全局拦截器异常", e);
            throw new BusinessException(ResponseCodeEnum.CODE_500);
        }
    }

//    @Around("requestInterceptor()")
//    public Object interceptorDo(ProceedingJoinPoint point)  {
//        try {
//            String methodName = point.getSignature().getName();
//            Object target = point.getTarget();
//            Object[] arguments = point.getArgs();
//            Class<?>[] parameterTypes = ((MethodSignature)point.getSignature()).getMethod().getParameterTypes();
//            Method method = target.getClass().getMethod(methodName, parameterTypes);
//            GlobalInterceptor interceptor = method.getAnnotation(GlobalInterceptor.class);
//
//            if (null == interceptor) {
//                return null;
//            }
//            // 校验登录
//            if (interceptor.checkLogin()) {
//
//            }
//            // 校验参数
//            if (interceptor.checkParams()) {
//                validateParams(method, arguments);
//            }
//            // 拦截器校验完，执行方法
//            Object pointResult = point.proceed();
//            return pointResult;
//        } catch (BusinessException e) {
//            logger.error("全局拦截器异常", e);
//            throw e;
//        } catch (Exception e) {
//            logger.error("全局拦截器异常", e);
//            throw new BusinessException(ResponseCodeEnum.CODE_500);
//        } catch (Throwable e) {
//            logger.error("全局拦截器异常", e);
//            throw new BusinessException(ResponseCodeEnum.CODE_500);
//        }
//    }

    private void validateParams(Method m, Object[] arguments) throws BusinessException {
        Parameter[] parameters = m.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            // 方法中的参数
            Parameter parameter = parameters[i];
            // 参数的值
            Object value = arguments[i];
            // 拿到参数上的注解
            VerifyParam verifyParam = parameter.getAnnotation(VerifyParam.class);
            // 如果某个参数不需要校验，就跳过它继续往下走
            if (verifyParam == null) {
                continue;
            }
            if (ArrayUtils.contains(TYPE_BASE, parameter.getParameterizedType().getTypeName())) {
                checkValue(value, verifyParam);
            } else {
                // 如果不是上面那种情况，说明参数是一个对象
                checkObjValue(parameter, value);
            }
        }
    }

    private void checkObjValue(Parameter parameter, Object value) {
        try {
            String typeName = parameter.getParameterizedType().getTypeName();
            Class clazz = Class.forName(typeName);
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                VerifyParam fieldVerifyParam = field.getAnnotation(VerifyParam.class);
                if (fieldVerifyParam == null) {
                    continue;
                }
                field.setAccessible(true);
                Object resultValue = field.get(value);
                checkValue(resultValue, fieldVerifyParam);
            }
        } catch (BusinessException e) {
            logger.error("校验参数失败", e);
            throw e;
        } catch (Exception e) {
            logger.error("校验参数失败", e);
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
    }

    /**
     * 校验参数
     * @param value 参数
     * @param verifyParam 带有@VerifyParam注解
     */
    private void checkValue(Object value, VerifyParam verifyParam) {
        Boolean isEmpty = value == null || StringTools.isEmpty(value.toString());
        Integer length = value == null ? 0 : value.toString().length();

        /**
         * 校验空
         * 如果你传进来的参数是空，但是这个参数又是必填的，那么报参数错
         */
        if (isEmpty && verifyParam.required()){
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        /**
         * 校验长度
         * 如果你的长度比要求的最小长度小或者比最大长度长，参数错误
         */
        if (!isEmpty && (verifyParam.max() != -1 && verifyParam.max() < length
                || verifyParam.min() != -1 && verifyParam.min() > length)) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        /**
         * 校验正则
         * 值不等于null并且正则不为空并且校验不过，报参数错误
         */
        if (!isEmpty && !StringTools.isEmpty(verifyParam.regex().getRegex())
                && !VerifyUtils.verify(verifyParam.regex(), String.valueOf(value))) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
    }

    private void checkLogin(Boolean checkAdmin) {
        // 怎样在一个类里拿到一个session信息
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        SessionWebUserDto userDto = (SessionWebUserDto)session.getAttribute(Constants.SESSION_KEY);
        if (null == userDto) {
            // session有默认的过期时间或者在其他地方设置了，所以session过期就拿不到里面的信息，就会进到这里
            throw new BusinessException(ResponseCodeEnum.CODE_901);
        }
        if (checkAdmin && !userDto.getAdmin()) {
            throw new BusinessException(ResponseCodeEnum.CODE_404);
        }

    }

}
